<?php
namespace mytp;

use Exception;
use PDO;
use PDOException;

class DB {
  // 数据库初始化设置
  protected static $initConfig = [];
  // 存放数据库的容器, instance: 实例
  protected static $instance;
  protected $config = [
    'type' => 'mysql',
    'host' => 'localhost',
    'port' => '3306',
    'charset' => 'utf8',
    'user' => 'root',
    'pwd' => '',
    'dbname' => '',
    'prefix' => ''
  ];
  protected $pdo;
  
  public static function getInstance() {
    if(!self::$instance) {
      self::$instance = new static(static::$initConfig);
    }
    return self::$instance;
  }

  public static function init(Array $config = []) {
    static::$initConfig = $config;
  }

  // 将用户设置的数据库选项和默认的合并
  public function __construct(Array $config = []) {
    $this->config = array_merge($this->config, $config);
    // 实例化PDO, 连接数据库
    $this->initPDO();
  }

  // 获取配置的函数
  public function getConfig($name = null) {
    return $name ? $this->config[$name] : $this->config;
  }

  public function initPDO() {
    $type = $this->getConfig('type');
    $host = $this->getConfig('host');
    $port = $this->getConfig('port');
    $dbname = $this->getConfig('dbname');
    $charset = $this->getConfig('charset');
    $user = $this->getConfig('user');
    $pwd = $this->getConfig('pwd');
    
    $dsn = "$type:host=$host;port=$port;dbname=$dbname;charset=$charset";
    
    try {
      $this->pdo = new PDO($dsn, $user, $pwd);
    } catch(PDOException $err) {
      // 抛出错误信息
      throw new Exception('数据库连接失败, 错误信息为: '.$err->getMessage());
    }
    // 设置PDO的错误模式为抛出异常
    $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }

  protected function errorMsg(Exception $e, $sql = '') {
    $error = $e->getMessage();
    if($sql != '') {
      $error .= " SQL语句执行失败: " . $sql;
    }
    return $error;
  }

  // 查询操作, 预处理机制的执行方法 execute
  // 查询一行
  public function fetchRow($sql, array $bind = []) {
    try {
      $stmt = $this->pdo->prepare($sql);
      $stmt->execute($bind);
      return $stmt->fetch(PDO::FETCH_ASSOC);
    } catch(PDOException $err) {
      throw new Exception($this->errorMsg($err, $sql));
    }
  }

  // 查询全部
  public function fetchAll($sql, array $bind = []) {
    try {
      $stmt = $this->pdo->prepare($sql);
      $stmt->execute($bind);
      return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch(PDOException $err) {
      throw new Exception($this->errorMsg($err, $sql)); 
    }
  }

  // 增, 删, 改
  public function execute($sql, array $bind = []) {
    try {
      $stmt = $this->pdo->prepare($sql);
      $stmt->execute($bind);
      // 返回受影响的行数
      return $stmt->rowCount();
    } catch(PDOException $err) {
      throw new Exception('SQL语句执行失败, 错误为: '.$err->getMessage()); 
    }
  }

  // 返回最后数据的 id
  public function lastInsertId() {
    return $this->pdo->lastInsertId();
  }

  // 
  public function sqlGetAll($sql) {
    try {
      echo "Good";
      // return $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch(PDOException $err) {
      throw new Exception($this->errorMsg($err, $sql)); 
    }
  }
}
?>